﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System;
using System.Collections.Immutable;
using System.Threading;

namespace CodeAnalysisRuleCollection.Maths.CSharp
{
    /// <example>a + 0</example>
    [DiagnosticAnalyzer]
    [ExportDiagnosticAnalyzer(DiagnosticId, LanguageNames.CSharp)]
    internal class BinaryOperationStatementHasNoEffectDiagnosticAnalyzer : ISyntaxNodeAnalyzer<SyntaxKind>
    {
        public const string DiagnosticId = "StatementHasNoEffectInMaths";

        public static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
            DiagnosticId,
            "Statement has no effect",
            "Statement can be removed, because it has no effect.",
            "Performance",
            DiagnosticSeverity.Warning
        );

        public ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        {
            get
            {
                return ImmutableArray.Create(Rule);
            }
        }

        public ImmutableArray<SyntaxKind> SyntaxKindsOfInterest
        {
            get
            {
                return ImmutableArray.Create(SyntaxKind.ExpressionStatement);
            }
        }

        public void AnalyzeNode(SyntaxNode node, SemanticModel semanticModel, Action<Diagnostic> addDiagnostic, CancellationToken cancellationToken)
        {
            // TODO: Handle when it was a body of a parent statement

            if ((var statement = node as ExpressionStatementSyntax) != null)
            if ((var binary = statement.Expression as BinaryExpressionSyntax) != null)
            {
                Optional<object> rightValue = semanticModel.GetConstantValue(binary.Right, cancellationToken);

                bool
                    isRightZero = rightValue.HasValue && rightValue.Value.Equals(0),
                    isRightOne = rightValue.HasValue && rightValue.Value.Equals(1)
                ;

                // a += 0, a -= 0
                if (
                    (binary.IsKind(SyntaxKind.AddAssignmentExpression) || binary.IsKind(SyntaxKind.SubtractAssignmentExpression)) &&
                    isRightZero
                )
                    addDiagnostic(Diagnostic.Create(Rule, node.GetLocation()));
                
                // a *= 1, a /= 1
                if (
                    (binary.IsKind(SyntaxKind.MultiplyAssignmentExpression) || binary.IsKind(SyntaxKind.DivideAssignmentExpression)) &&
                    isRightOne
                )
                    addDiagnostic(Diagnostic.Create(Rule, node.GetLocation()));
            }
        }
    }
}
